package org.bes.utils;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.bes.WinClass.FileManager;
import org.bes.api.IExtractor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.json.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



import static org.bes.WinClass.FileManager.fileListQueue;
import static org.bes.utils.StrPathUtils.VERSION_PATTERN;

public class ExtractorUtils implements IExtractor {
    private static final Logger log = LoggerFactory.getLogger(ExtractorUtils.class);


    public void extract() throws IOException {
        Path compressFilePath = FileManager.getPatchFilePath();
        // Extract the compressed file
        if (compressFilePath.toString().endsWith(".tar")) {
            log.info("Extracting tar file: {}", compressFilePath);
        } else if (compressFilePath.toString().endsWith(".zip")) {
            log.info("Extracting zip file: {}", compressFilePath);
            extractZip(compressFilePath);
        } else {
            log.error("Unsupported file type: {}", compressFilePath);
        }
    }

    public void extractZip(Path compressFilePath) throws IOException {
        // Create the backup directory to extract the files into
        Path backupDir = compressFilePath.getParent().resolve("backup");
        Files.createDirectories(backupDir);
        log.debug("Created backup directory: {}", backupDir);

        try (InputStream inputStream = new FileInputStream(compressFilePath.toFile());
             ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(inputStream)) {

            ZipArchiveEntry zipEntry;
            while ((zipEntry = zipInputStream.getNextZipEntry()) != null) {
                String zipEntryName = zipEntry.getName();
                File file = new File(backupDir.toFile(), zipEntryName);  // Create the target file under the backup directory

                // Extract file contents to the corresponding file in the backup folder
                try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file))) {
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = zipInputStream.read(buffer)) > 0) {
                        bufferedOutputStream.write(buffer, 0, length);
                    }
                    log.debug("Extracted file: {}", file.getAbsolutePath());
                }

                // Recursively extract nested ZIP files, but force extraction into the backup directory
                if (StrPathUtils.getExtension(file.getName()).equals("zip")) {
                    log.info("Found nested zip file: {}", file.getAbsolutePath());

                    // Handle nested zip file extraction
                    List<File> extractedFiles = extractZipToBackup(file.toPath(), backupDir
                            .resolve(StrPathUtils.getFileNameWithoutExtension(Path.of(file.getName()))));
                    for(File extractedFile : extractedFiles){

                        fileListQueue.add(new AbstractMap.SimpleEntry<>("+", extractedFile));

                    }


                    // Delete nested zip file after extraction
                    if (file.delete()) {
                        log.debug("Deleted nested zip file: {}", file.getAbsolutePath());
                    } else {
                        log.warn("Failed to delete nested zip file: {}", file.getAbsolutePath());
                    }
                } else {
                    // Add regular files to fileListQueue under the "+" key
                    fileListQueue.add(new AbstractMap.SimpleEntry<>("+", file));
                }

            }
        } catch (IOException e) {
            log.error("Decompression failed: {}. {}", compressFilePath, e.getMessage(), e);
        }



    }

    private List<File> extractZipToBackup(Path zipFilePath, Path targetDir) throws IOException {
        List<File> extractedFiles = new ArrayList<>();

        try (InputStream inputStream = new FileInputStream(String.valueOf(zipFilePath));
             ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(inputStream)) {

            ZipArchiveEntry zipEntry;
            while ((zipEntry = zipInputStream.getNextZipEntry()) != null) {
                String zipEntryName = zipEntry.getName();
                File file = new File(targetDir.toFile(), zipEntryName);  // Force extraction into the backup directory

                // If the entry is a directory, create the directory in the backup folder
                if (zipEntry.isDirectory()) {
                    if (file.mkdirs()) {
                        log.debug("Created directory: {}", file.getAbsolutePath());
                    } else {
                        log.warn("Failed to create directory: {}", file.getAbsolutePath());
                    }
                } else {
                    // Extract file contents to the corresponding file in the backup folder
                    try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file))) {
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = zipInputStream.read(buffer)) > 0) {
                            bufferedOutputStream.write(buffer, 0, length);
                        }

                    }

                    if ((StrPathUtils.getExtension(file.getName()).equals("json"))) {
                        // Check if the directory name matches the V+number pattern
                        Matcher matcher = VERSION_PATTERN.matcher(file.getAbsolutePath());
                        if (matcher.find()) {
                            String version = matcher.group(1);
                            //FileManager.versionDirName.add(zipEntryName);
                            // Add directory to list if it matches
                            log.info("Found json file: {}", file.getAbsolutePath());
                            //jsonRead(file);
                            // Add regular files to fileListQueue under the "-" key
                            fileListQueue.add(new AbstractMap.SimpleEntry<>("-", file));
                        }
                    }else {
                        log.debug("Extracted file: {}", file.getAbsolutePath());
                        extractedFiles.add(file);
                    }


                }
            }
        } catch (IOException e) {
            log.error("Decompression failed for nested zip: {}. {}", zipFilePath, e.getMessage(), e);
        }

        return extractedFiles;
    }




    //public List<File> extractTar(Path compressFilePath) throws IOException {
    //    int dotIndex = StrPathUtils.getDotIndex(compressFilePath.getFileName().toString());
    //    Path fileName = Path.of(compressFilePath.getFileName().toString().substring(0, dotIndex));
    //    Path newFileDirectory = compressFilePath.getParent().resolve(fileName);
    //    Files.createDirectories(newFileDirectory);
    //
    //    List<String> versionDirs = new ArrayList<>();  // List to hold folder names with V+number pattern
    //
    //    try (InputStream inputStream = new FileInputStream(String.valueOf(compressFilePath))) {
    //        TarArchiveInputStream tarInputStream = new TarArchiveInputStream(inputStream);
    //        TarArchiveEntry tarEntry;
    //
    //        while ((tarEntry = tarInputStream.getNextTarEntry()) != null) {
    //            String tarEntryName = tarEntry.getName();
    //
    //            File file = new File(String.valueOf(newFileDirectory.resolve(tarEntryName)));
    //
    //            // If the entry is a directory, create the directory
    //            if (tarEntry.isDirectory()) {
    //                if (file.mkdirs()) {
    //                    log.debug("Created directory: {}", file.getAbsolutePath());
    //                } else {
    //                    log.warn("Failed to create directory: {}", file.getAbsolutePath());
    //                }
    //
    //                // Check if the directory name matches the V+number pattern
    //                Matcher matcher = VERSION_PATTERN.matcher(file.getName());
    //                if (matcher.find()) {
    //                    versionDirs.add(file.getName());  // Add directory to list if it matches
    //                }
    //            } else {
    //                FileManager.fileList.add(file);
    //            }
    //
    //            FileManager.fileList.add(file);
    //        }
    //    } catch (IOException e) {
    //        log.error("Decompression failed for " + compressFilePath + ": " + e.getMessage(), e);
    //    }
    //
    //    // Sort the version directories
    //    Collections.sort(versionDirs);  // Sort alphabetically (this will sort V1, V2, V10, etc.)
    //    log.info("Sorted version folders: {}", versionDirs);
    //
    //    return FileManager.fileList;
    //}
}
